package jmine.tec.web.wicket.component.command.button;

import static jmine.tec.web.wicket.spring.SpringBeanReference.forName;

import java.io.Serializable;
import java.util.Set;

import jmine.tec.web.wicket.BeanReference;
import jmine.tec.web.wicket.exception.ExceptionHandler;
import jmine.tec.web.wicket.pages.form.WebFormPagesMessages;

import org.apache.wicket.Page;
import org.apache.wicket.Session;

import bancosys.tec.exception.LocalizedMessageHolder;

/**
 * Implementação base de botão utilizado para executar ações
 * 
 * @author Rafael Volpato
 * @date Nov 17, 2011
 */
public abstract class ExecutionButton extends AbstractButtonCommand {

    private final BeanReference<ExceptionHandler> exceptionHandler = forName("wicketExceptionHandler");

    private int successCount = 0;

    private int errorCount = 0;

    /**
     * Callback para execução do comando
     * 
     * @return <code>null</code>
     */
    @Override
    public final Page execute() {
        this.successCount = 0;
        this.errorCount = 0;

        Set<Serializable> selected = this.getSelected();
        if (selected.isEmpty()) {
            Session.get().getFeedbackMessages().error(null, this.getErrorNoEntitySelected().getMessage());
            Session.get().dirty();
            return null;
        }

        try {
            this.executeSelected(selected);
        } catch (Exception e) {
            this.exceptionHandler.get().handle(e);
        }

        if (this.errorCount > 0) {
            Session.get().getFeedbackMessages().error(null, this.getErrorMessage(this.errorCount).getMessage());
            Session.get().dirty();
        }

        if (this.successCount > 0) {
            Session.get().getFeedbackMessages().info(null, this.getSucessMessage(this.successCount).getMessage());
            Session.get().dirty();
        }

        return null;
    }

    /**
     * Incrementa o contador de execuções que são concluidas com sucesso.
     */
    protected final void executionSucessfull() {
        this.successCount++;
    }

    /**
     * Incrementa o contador de execuções que são concluidas com erro.
     * 
     * @param e exception
     */
    protected final void executionFailed(Exception e) {
        this.exceptionHandler.get().handle(e);
        this.errorCount++;
    }

    /**
     * Retorna a mensagem que deve ser exibida se nenhuma entidade estiver selecionada.
     * 
     * @return a mensagem que deve ser exibida se nenhuma entidade estiver selecionada
     */
    protected LocalizedMessageHolder getErrorNoEntitySelected() {
        return WebFormPagesMessages.ERROR_EXECUTION_NO_ENTITY_SELECTED.create();
    }

    /**
     * Retorna a mensagem que deve ser exibida após a execução das ações, caso ocorra algum erro.
     * 
     * @param count quantidade de entidades cuja ação foi executada com erro
     * @return a mensagem que deve ser exibida após a execução das ações, caso ocorra algum erro
     */
    protected LocalizedMessageHolder getErrorMessage(int count) {
        if (count == 1) {
            return WebFormPagesMessages.ERROR_EXECUTION_SINGLE_FAILED.create();
        } else {
            return WebFormPagesMessages.ERROR_EXECUTION_MULTIPLE_FAILED.create(count);
        }
    }

    /**
     * Retorna a mensagem que deve ser exibida após a execução das ações, caso ocorra algum sucesso.
     * 
     * @param count quantidade de entidades cuja ação foi executada com sucesso
     * @return a mensagem que deve ser exibida após a execução das ações, caso ocorra algum sucesso
     */
    protected LocalizedMessageHolder getSucessMessage(int count) {
        if (count == 1) {
            return WebFormPagesMessages.INFO_EXECUTION_SINGLE_SUCCESS.create();
        } else {
            return WebFormPagesMessages.INFO_EXECUTION_MULTIPLE_SUCCESS.create(count);
        }
    }

    /**
     * Executa a ação para a entidade.
     * 
     * @param selected set de id selecionados
     * @throws Exception se ocorrer algum erro
     */
    protected abstract void executeSelected(Set<Serializable> selected) throws Exception;

    /**
     * Retorna as entidade que foram selecionadas.
     * 
     * @return entidades selecionadas
     */
    protected abstract Set<Serializable> getSelected();
}
